package guestbook;

import java.util.*;
import com.google.appengine.api.datastore.*;
import com.google.appengine.api.datastore.Query.*;

public class ConsistentResults extends AbstractList<Entity> implements Iterator<Entity>
{
	public ConsistentResults(Context context, List<Entity> results, Query query)
	{
		this.results = results;
		this.context = context;
		this.query = query;
	}

	private final Query query;
	private final Context context;
	private final List<Entity> results;

	private boolean hasBeenProcessed = false;

	private void process()
	{
		if (hasBeenProcessed)
			return;

		hasBeenProcessed = true;

		if (context.putHelper.entities == null || context.putHelper.entities.isEmpty())
			return;

		List<Entity> entities = context.putHelper.entities;

		for (Entity e : entities)
		{
			if (query.getKind().equals(e.getKind()))
			{
				boolean belongs = true;
				for (FilterPredicate fp : query.getFilterPredicates())
				{
					if (fp.getOperator().equals(Query.FilterOperator.EQUAL))
					{
						if (!(e.getProperty(fp.getPropertyName()) == fp.getValue()))
						{
							belongs = false;
							break;
						}
					}
					else if (fp.getOperator().equals(Query.FilterOperator.NOT_EQUAL))
					{
						if (!(e.getProperty(fp.getPropertyName()) != fp.getValue()))
						{
							belongs = false;
							break;
						}
					}
				}
				if (belongs)
					mergeWithResults(e);
			}
		}
	}

	private void mergeWithResults(Entity e)
	{
		if (query.getSortPredicates().size() == 0)
		{
			results.add(e);
			return;
		}

		for (int i = 0; i < results.size(); i++)
		{
			if (e.getKey().equals(e.getKey()))
				return;

			for (SortPredicate sp : query.getSortPredicates())
			{
				Comparable pobj = (Comparable) e.getProperty(sp.getPropertyName());

				Comparable cobj = (Comparable) results.get(i).getProperty(sp.getPropertyName());
				Comparable nobj;

				if (i < results.size() - 1)
					nobj = (Comparable) results.get(i + 1).getProperty(sp.getPropertyName());
				else
				{
					results.add(e);
					return;
				}

				int compareToC = pobj.compareTo(cobj);
				int compareToN = pobj.compareTo(nobj);

				if (sp.getDirection().equals(Query.SortDirection.ASCENDING))
				{
					if (compareToC == 0)
					{
						results.add(i, e);
						return;
					}
					else if (compareToC < 0)
					{
						results.add(i - 1, e);
						return;
					}
					else if (compareToN < 0)
					{
						results.add(i, e);
						return;
					}
				}
				else if (sp.getDirection().equals(Query.SortDirection.DESCENDING))
				{
					if (compareToC == 0)
					{
						results.add(i, e);
						return;
					}
					else if (compareToC < 0)
					{
						results.add(i - 1, e);
						return;
					}
					else if (compareToN < 0)
					{
						results.add(i, e);
						return;
					}
				}
			}
		}

		results.add(e);
	}

	@Override
	public Entity get(int index)
	{
		process();
		return results.get(index);
	}

	@Override
	public int size()
	{
		process();
		return results.size();
	}

	private int iteratorIndex = 0;

	@Override
	public boolean hasNext()
	{
		process();
		return iteratorIndex + 1 < size();
	}

	@Override
	public Entity next()
	{
		process();
		return get(iteratorIndex++);
	}

	@Override
	public void remove()
	{
		throw new UnsupportedOperationException();
	}
}
